package com.yh.csx.business.core.aspect;

import com.alibaba.fastjson.JSON;
import com.yh.csx.bsf.core.thread.ThreadPool;
import com.yh.csx.bsf.core.util.ExceptionUtils;
import com.yh.csx.bsf.core.util.WebUtils;
import com.yh.csx.bsf.health.base.AbstractCollectTask;
import com.yh.csx.bsf.health.base.Message;
import com.yh.csx.business.api.exception.BusinessException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;

import java.lang.reflect.Method;

/**
 * @创建人 霍钧城
 * @创建时间 2020年12月03日 17:28:00
 * @描述 Controller全局异常上报（业务、RPC）
 */
@Slf4j
@Aspect
public class WebExceptionAspect {

    @Value("${bsf.web.exception.enabled:false}")
    private boolean webExceptionEnabled;

    @Around("within(@org.springframework.web.bind.annotation.RestController *)")
    public Object handleRestController(ProceedingJoinPoint point) throws Throwable {
        return checkException(point);
    }

    @Around("within(@org.springframework.stereotype.Controller *)")
    public Object handleController(ProceedingJoinPoint point) throws Throwable {
        return checkException(point);
    }

    private Object checkException(ProceedingJoinPoint point) throws Throwable {
        if (!webExceptionEnabled) {
            return point.proceed();
        } else {
            try {
                return point.proceed();
            } catch (Throwable e) {
                if (!(e instanceof BusinessException)) {
                    reportException(point, e.getMessage()+"\nFullStackTrace:"+ExceptionUtils.getFullStackTrace(e));
                }
                throw e;
            }
        }
    }

    private void reportException(ProceedingJoinPoint point, String returnJson)
    {
        ExceptionUtils.ignoreException(()->{
            String url=getUrl(point);
            String inputParams=getArgs(point);
            ThreadPool.System.submit("上报异常", () -> {
                AbstractCollectTask.notifyMessage(new Message("异常",JSON.toJSONString(new BeException(url,inputParams, returnJson))));
            });
        },true);
    }

    private String getUrl(ProceedingJoinPoint point) {
        String url = WebUtils.getRequestURI();
        if (StringUtils.isNotBlank(url)) {
            return url;
        } else {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            return method.getDeclaringClass().getSimpleName() + ('.') + (method.getName());
        }
    }

    private String getArgs(ProceedingJoinPoint point) {
        StringBuilder sb = new StringBuilder();
        MethodSignature signature = (MethodSignature) point.getSignature();
        Object[] values = point.getArgs();
        if (values.length == 0) {
            sb.append("{}");
        } else {
            sb.append("{");
            String[] names = signature.getParameterNames();
            for (int i = 0; i < values.length; i++) {
                if (i > 0) sb.append(", ");
                sb.append(names[i]).append("=").append(JSON.toJSONString(values[i]));
            }
            sb.append("}");
        }
        return sb.toString();
    }

    @AllArgsConstructor
    private static class BeException {
        public String url;
        public String inputParams;
        public String outputParams;
    }
}

